﻿using CgdataBase;
using FolderCompare.Helpers;
using FolderCompare.Models;
using FolderCompare.Views;
using Prism.Commands;
using Prism.Events;
using Prism.Mvvm;
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using static FolderCompare.Properties.Resources;
using FolderBrowserDialog = System.Windows.Forms.FolderBrowserDialog;
using MessageBox = FolderCompare.Views.MessageBox;

namespace FolderCompare.ViewModels
{
    public class MainWindowViewModel : BindableBase, IBusyScope
    {
        private readonly MainWindow _win;
        private Prism.Events.IEventAggregator Aggregator => EventHelper.Aggregator;
        private DataHelper DbHelper => DataHelper.Instance;
        private AppSettings Settings => AppSettings.Instance;
        public int FontSize => Settings.FontSize;
        public string FontName => Settings.FontName;

        public string Title { get; set; }
        public bool Topmost { get; set; }
        public string[] TextFileTypes { get; set; }

        public bool IsBusy { get; set; }
        public string BusyContent { get; set; }
        public ConcurrentStack<bool> BusyStack { get; set; } = new ConcurrentStack<bool>();

        public HeaderInfo CurrentHeader => StackItems.Last();
        public ObservableCollection<HeaderInfo> StackItems { get; set; }

        public List<string> LeftRootItems { get; set; }
        public string LeftRootPath { get; set; }
        public CgDirectoryInfo LeftRoot { get; set; }

        public List<string> RightRootItems { get; set; }
        public string RightRootPath { get; set; }
        public CgDirectoryInfo RightRoot { get; set; }

        private CgDirectoryInfo _currentLeft;
        private CgDirectoryInfo _currentRight;

        public ObservableCollection<CgFileSystemInfo> LeftItems { get; set; }
        public CgFileSystemInfo SelectedLeft { get; set; }
        public ObservableCollection<CgFileSystemInfo> RightItems { get; set; }
        public CgFileSystemInfo SelectedRight { get; set; }
        public int SelectedIndex { get; set; } = -1;

        public IEnumerable SelectedLefts { get; set; }
        public IEnumerable SelectedRights { get; set; }

        public EShowType ShowType { get; set; }

        public DelegateCommand BackUpCommand { get; private set; }

        public DelegateCommand SelectLeftRootCommand { get; }
        public DelegateCommand SelectRightRootCommand { get; }

        public DelegateCommand OpenFolderLeftCommand { get; }
        public DelegateCommand CopyToRightCommand { get; }
        public DelegateCommand CopyNameLeftCommand { get; }
        public DelegateCommand CopyFullNameLeftCommand { get; }
        public DelegateCommand DeleteLeftCommand { get; }
        public DelegateCommand ExportLeftCommand { get; }
        public DelegateCommand RefreshLeftCommand { get; }

        public DelegateCommand OpenFolderRightCommand { get; }
        public DelegateCommand CopyToLeftCommand { get; }
        public DelegateCommand CopyNameRightCommand { get; }
        public DelegateCommand CopyFullNameRightCommand { get; }
        public DelegateCommand DeleteRightCommand { get; }
        public DelegateCommand ExportRightCommand { get; }
        public DelegateCommand RefreshRightCommand { get; }
        public DelegateCommand RefreshCommand { get; }

        public DelegateCommand ShowAllCommand { get; }
        public DelegateCommand ShowNotEqualCommand { get; }
        public DelegateCommand ShowEqualsCommand { get; }

        public DelegateCommand<MouseButtonEventArgs> LeftDoubleClickCommand { get; }
        public DelegateCommand<MouseButtonEventArgs> RightDoubleClickCommand { get; }
        public DelegateCommand<DragEventArgs> LeftDropCommand { get; private set; }
        public DelegateCommand<DragEventArgs> RightDropCommand { get; private set; }

        public DelegateCommand LoadedCommand { get; }
        public DelegateCommand<CancelEventArgs> ClosingCommand { get; private set; }
        public DelegateCommand AboutCommand { get; }
        public DelegateCommand SettingsCommand { get; }

        public MainWindowViewModel(MainWindow win)
        {
            _win = win;
            Title = ApplicationName;
            TextFileTypes = GetTextFileTypes();

            SelectLeftRootCommand = new DelegateCommand(OnSelectLeftRoot);
            SelectRightRootCommand = new DelegateCommand(OnSelectRightRoot);

            OpenFolderLeftCommand = new DelegateCommand(OnOpenFolderLeft, CanEditLeft).ObservesProperty(() => SelectedLeft);
            CopyToRightCommand = new DelegateCommand(OnCopyToRight, CanEditLeft).ObservesProperty(() => SelectedLeft);
            ExportLeftCommand = new DelegateCommand(OnExportLeft, CanEditLeft).ObservesProperty(() => SelectedLeft);
            CopyNameLeftCommand = new DelegateCommand(OnCopyNameLeft, CanEditLeft).ObservesProperty(() => SelectedLeft);
            CopyFullNameLeftCommand = new DelegateCommand(OnCopyFullNameLeft, CanEditLeft).ObservesProperty(() => SelectedLeft);

            DeleteLeftCommand = new DelegateCommand(OnDeleteLeft, CanEditLeft).ObservesProperty(() => SelectedLeft);
            RefreshLeftCommand = new DelegateCommand(OnRefreshLeft);

            OpenFolderRightCommand = new DelegateCommand(OnOpenFolderRight, CanEditRight).ObservesProperty(() => SelectedLeft);
            CopyToLeftCommand = new DelegateCommand(OnCopyToLeft, CanEditRight).ObservesProperty(() => SelectedLeft);
            ExportRightCommand = new DelegateCommand(OnExportRight, CanEditRight).ObservesProperty(() => SelectedLeft);
            CopyNameRightCommand = new DelegateCommand(OnCopyNameRight, CanEditRight).ObservesProperty(() => SelectedLeft);
            CopyFullNameRightCommand = new DelegateCommand(OnCopyFullNameRight, CanEditRight).ObservesProperty(() => SelectedLeft);

            DeleteRightCommand = new DelegateCommand(OnDeleteRight, CanEditRight).ObservesProperty(() => SelectedRight);
            RefreshRightCommand = new DelegateCommand(OnRefreshRight);
            RefreshCommand = new DelegateCommand(OnRefresh);

            ShowAllCommand = new DelegateCommand(OnShowAll);
            ShowNotEqualCommand = new DelegateCommand(OnShowNotEqual);
            ShowEqualsCommand = new DelegateCommand(OnShowEquals);

            LoadedCommand = new DelegateCommand(OnLoaded);
            ClosingCommand = new DelegateCommand<CancelEventArgs>(OnClosing);

            BackUpCommand = new DelegateCommand(OnBackUp);

            LeftDoubleClickCommand = new DelegateCommand<MouseButtonEventArgs>(OnLeftDoubleClick);
            RightDoubleClickCommand = new DelegateCommand<MouseButtonEventArgs>(OnRightDoubleClick);

            LeftDropCommand = new DelegateCommand<DragEventArgs>(OnLeftDrop);
            RightDropCommand = new DelegateCommand<DragEventArgs>(OnRightDrop);

            AboutCommand = new DelegateCommand(OnAbout);
            SettingsCommand = new DelegateCommand(OnSettings);

            Aggregator.GetEvent<SwitchHeaderEvent>().Subscribe(OnSwitchHeader, ThreadOption.UIThread);
        }

        private string[] GetTextFileTypes()
        {
            return Settings.TextFileTypes.StringSplit("\r\n");
        }

        private void OnOpenFolderRight()
        {
            Process.Start("explorer.exe", "/select," + SelectedRight.FullName);
        }

        private void OnOpenFolderLeft()
        {
            Process.Start("explorer.exe", "/select," + SelectedLeft.FullName);
        }

        private async void OnCopyToLeft()
        {
            if (_currentRight == null)
            {
                MessageBox.Show("左侧路径不存在");
                return;
            }

            var items = SelectedRights.Cast<CgFileSystemInfo>().ToArray();
            var message = $"是否把选中的 {items.Length} 项复制到左侧？";
            if (MessageBox.Show(message, "提示", MessageBoxButton.OKCancel) == MessageBoxResult.OK)
            {
                using (var scope = new BusyScope(this))
                {
                    var dirPath = _currentLeft.FullName;
                    var cts = new CancellationTokenSource();
                    await Task.Run(() =>
                    {
                        foreach (CgFileSystemInfo item in items)
                        {
                            if (item is CgFileInfo file)
                            {
                                File.Copy(file.FullName, Path.Combine(dirPath, file.Name), true);
                            }
                            else if (item is CgDirectoryInfo dir)
                            {
                                FileSystemHelper.CopyFolder(dir.FullName, Path.Combine(dirPath, dir.Name), cts.Token);
                            }
                        }
                    });
                }
            }
        }

        private async void OnCopyToRight()
        {
            if (_currentRight == null)
            {
                MessageBox.Show("右侧路径不存在");
                return;
            }

            var items = SelectedLefts.Cast<CgFileSystemInfo>().ToArray();
            var message = $"是否把选中的 {items.Length} 项复制到右侧？";
            if (MessageBox.Show(message, "提示", MessageBoxButton.OKCancel) == MessageBoxResult.OK)
            {
                using (var scope = new BusyScope(this))
                {
                    var dirPath = _currentRight.FullName;
                    var cts = new CancellationTokenSource();
                    await Task.Run(() =>
                    {
                        foreach (CgFileSystemInfo item in items)
                        {
                            if (item is CgFileInfo file)
                            {
                                File.Copy(file.FullName, Path.Combine(dirPath, file.Name), true);
                            }
                            else if (item is CgDirectoryInfo dir)
                            {
                                FileSystemHelper.CopyFolder(dir.FullName, Path.Combine(dirPath, dir.Name), cts.Token);
                            }
                        }
                    });
                }
            }
        }

        private async void OnExportRight()
        {
            var dialog = new FolderBrowserDialog();
            if (dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                var dirPath = dialog.SelectedPath;
                var items = SelectedRights.Cast<CgFileSystemInfo>().ToArray();

                using (var scope = new BusyScope(this))
                {
                    var cts = new CancellationTokenSource();
                    await Task.Run(() =>
                    {
                        foreach (CgFileSystemInfo item in items)
                        {
                            if (item is CgFileInfo file)
                            {
                                File.Copy(file.FullName, Path.Combine(dirPath, file.Name), true);
                            }
                            else if (item is CgDirectoryInfo dir)
                            {
                                FileSystemHelper.CopyFolder(dir.FullName, Path.Combine(dirPath, dir.Name), cts.Token);
                            }
                        }
                    });
                }

                MessageBox.Show("导出成功");
            }
        }

        private async void OnExportLeft()
        {
            var dialog = new FolderBrowserDialog();
            if (dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                var dirPath = dialog.SelectedPath;
                var items = SelectedLefts.Cast<CgFileSystemInfo>().ToArray();

                using (var scope = new BusyScope(this))
                {
                    var cts = new CancellationTokenSource();
                    await Task.Run(() =>
                    {
                        foreach (CgFileSystemInfo item in items)
                        {
                            if (item is CgFileInfo file)
                            {
                                File.Copy(file.FullName, Path.Combine(dirPath, file.Name), true);
                            }
                            else if (item is CgDirectoryInfo dir)
                            {
                                FileSystemHelper.CopyFolder(dir.FullName, Path.Combine(dirPath, dir.Name), cts.Token);
                            }
                        }
                    });
                }

                MessageBox.Show("导出成功");
            }
        }

        private void OnCopyFullNameRight()
        {
            var items = SelectedRights.Cast<CgFileSystemInfo>().ToArray();
            ClipboardEx.SetText(items.Select(s => s.FullName).StringJoin("\r\n"));
        }

        private void OnCopyNameRight()
        {
            var items = SelectedRights.Cast<CgFileSystemInfo>().ToArray();
            ClipboardEx.SetText(items.Select(s => s.Name).StringJoin("\r\n"));
        }

        private void OnCopyFullNameLeft()
        {
            var items = SelectedLefts.Cast<CgFileSystemInfo>().ToArray();
            ClipboardEx.SetText(items.Select(s => s.FullName).StringJoin("\r\n"));
        }

        private void OnCopyNameLeft()
        {
            var items = SelectedLefts.Cast<CgFileSystemInfo>().ToArray();
            ClipboardEx.SetText(items.Select(s => s.Name).StringJoin("\r\n"));
        }

        private void OnRightDrop(DragEventArgs e)
        {
            var fileNames = (string[])e.Data.GetData(DataFormats.FileDrop, false);
            var dirName = fileNames[0];
            if (Directory.Exists(dirName))
            {
                UpdateRightRootPath(dirName);
            }
        }

        private void OnLeftDrop(DragEventArgs e)
        {
            var fileNames = (string[])e.Data.GetData(DataFormats.FileDrop, false);
            var dirName = fileNames[0];
            if (Directory.Exists(dirName))
            {
                UpdateLeftRootPath(dirName);
            }
        }

        private void OnShowAll()
        {
            ShowType = EShowType.显示全部;
            ResetLeftRightItems();
        }

        private void OnShowNotEqual()
        {
            ShowType = EShowType.显示差异项;
            ResetLeftRightItems();
        }

        private void OnShowEquals()
        {
            ShowType = EShowType.显示相同项;
            ResetLeftRightItems();
        }

        private void OnRefresh()
        {
            LoadLeftRoot();
            LoadRightRoot();
            CompareLeftRightRoot();
            ResetLeftRightItems();
        }

        private void CompareLeftRightRoot()
        {
            if (LeftRoot == null || RightRoot == null)
                return;

            CompareHelper.CompareDirectory(LeftRoot, RightRoot);
        }

        private void OnClosing(CancelEventArgs e)
        {
            ObjectHelper.DeepCopy(this, Settings);
        }

        private void OnLoaded()
        {
            ObjectHelper.DeepCopy(Settings, this);
        }

        private void OnBackUp()
        {
            if (StackItems.Count > 1)
            {
                StackItems.RemoveAt(StackItems.Count - 1);
            }

            ReloadLeftRightItems();
        }

        private void OnSwitchHeader(HeaderInfo info)
        {
            while (info.Index != StackItems.Last().Index)
            {
                StackItems.RemoveAt(StackItems.Count - 1);
            }

            ReloadLeftRightItems();
        }

        private void OnLeftDoubleClick(MouseButtonEventArgs e)
        {
            var list = e.Source as ItemsControl;
            var item = UIElementHelper.GetElementFromPoint(list, e.GetPosition(list));
            if (item != null && CanEditLeft())
            {
                DoubleClickItem(SelectedLeft);
            }
        }

        private void DoubleClickItem(CgFileSystemInfo info)
        {
            if (info is CgDirectoryInfo dir)
            {
                StackItems.Add(new HeaderInfo() { Header = dir.Name, Index = StackItems.Count });
                ReloadLeftRightItems();
            }
            else if (info is CgFileInfo file)
            {
                if (IsTextFile(file) && AllContainsFile(file.Index))
                {
                    new WinTextCompare(LeftItems.Single(s => s.Index == file.Index).FullName, RightItems.Single(s => s.Index == file.Index).FullName) { Owner = App.GetActiveWindow() }.ShowDialog();
                }
                else
                {
                    Process.Start(new ProcessStartInfo(file.FullName) { UseShellExecute = true });
                }
            }
            else
            {
                Debug.Assert(false);
            }
        }

        private bool AllContainsFile(int index)
        {
            return LeftItems.SingleOrDefault(s => s.Index == index)?.FullName.IsContainValidText() == true && RightItems.SingleOrDefault(s => s.Index == index)?.FullName.IsContainValidText() == true;
        }

        private bool IsTextFile(CgFileInfo file)
        {
            var extName = Path.GetExtension(file.FullName);

            return TextFileTypes.Contains(extName);
        }

        private bool CanEditLeft()
        {
            return SelectedLeft != null;
        }

        private void OnRightDoubleClick(MouseButtonEventArgs e)
        {
            var list = e.Source as ItemsControl;
            var item = UIElementHelper.GetElementFromPoint(list, e.GetPosition(list));
            if (item != null && CanEditRight())
            {
                DoubleClickItem(SelectedRight);
            }
        }

        private bool CanEditRight()
        {
            return SelectedRight != null;
        }

        private void ResetLeftRightItems()
        {
            StackItems = new ObservableCollection<HeaderInfo> { new HeaderInfo() { Header = "" } };

            ReloadLeftRightItems();
        }

        private void ReloadLeftRightItems()
        {
            _currentLeft = GetCurrentDirectory(LeftRoot);
            _currentRight = GetCurrentDirectory(RightRoot);

            switch (ShowType)
            {
                case EShowType.显示全部:
                    LeftItems = _currentLeft?.Children;
                    RightItems = _currentRight?.Children;
                    break;
                case EShowType.显示差异项:
                    LeftItems = _currentLeft?.Children.Where(s => s.Result != ECompareResult.匹配).ToObservableCollection();
                    RightItems = _currentRight?.Children.Where(s => s.Result != ECompareResult.匹配).ToObservableCollection();
                    break;
                case EShowType.显示相同项:
                    LeftItems = _currentLeft?.Children.Where(s => s.Result == ECompareResult.匹配).ToObservableCollection();
                    RightItems = _currentRight?.Children.Where(s => s.Result == ECompareResult.匹配).ToObservableCollection();
                    break;
                default:
                    break;
            }
        }

        private void OnDeleteLeft()
        {
            if (SelectedLeft == null)
                return;

            AppBase.DeleteItems(SelectedLefts.Cast<CgFileSystemInfo>().ToArray(), _currentLeft, _currentRight);
            ReloadLeftRightItems();
        }

        private void OnDeleteRight()
        {
            if (SelectedRight == null)
                return;

            AppBase.DeleteItems(SelectedRights.Cast<CgFileSystemInfo>().ToArray(), _currentRight, _currentLeft);
            ReloadLeftRightItems();
        }

        private void OnRefreshLeft()
        {
            LoadLeftRoot();
            RemoveEmptyItem(RightRoot);
            CompareLeftRightRoot();

            ResetLeftRightItems();
        }

        private void RemoveEmptyItem(CgDirectoryInfo info)
        {
            if (info == null)
                return;

            if (info.Children.HadItems())
            {
                foreach (var item in info.Children.ToArray())
                {
                    if (item.Name.IsNullOrEmpty())
                    {
                        info.Children.Remove(item);
                    }
                    else
                    {
                        if (item is CgDirectoryInfo dir)
                        {
                            RemoveEmptyItem(dir);
                        }
                    }
                }
            }
        }

        private void LoadLeftRoot()
        {
            if (LeftRootPath.IsContainValidText() && Directory.Exists(LeftRootPath))
            {
                LeftRoot = new CgDirectoryInfo(new DirectoryInfo(LeftRootPath));
            }
        }

        private CgDirectoryInfo GetCurrentDirectory(CgDirectoryInfo info)
        {
            if (info == null)
            {
                return null;
            }

            for (int i = 1; i < StackItems.Count; i++)
            {
                if (info != null)
                {
                    info = GetDirectoryInfo(info, StackItems[i].Header);
                }
                else
                {
                    break;
                }
            }

            return info;
        }

        private CgDirectoryInfo GetDirectoryInfo(CgDirectoryInfo info, string name)
        {
            return info.Children.OfType<CgDirectoryInfo>().FirstOrDefault(s => s.Name == name);
        }

        private void OnRefreshRight()
        {
            LoadRightRoot();
            RemoveEmptyItem(LeftRoot);
            CompareLeftRightRoot();

            ResetLeftRightItems();
        }

        private void LoadRightRoot()
        {
            if (RightRootPath.IsContainValidText() && Directory.Exists(RightRootPath))
            {
                RightRoot = new CgDirectoryInfo(new DirectoryInfo(RightRootPath));
            }
        }

        private void OnSelectLeftRoot()
        {
            var dialog = new FolderBrowserDialog();
            if (dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                UpdateLeftRootPath(dialog.SelectedPath);
            }
        }

        private void UpdateLeftRootPath(string path)
        {
            LeftRootPath = path;
            OnRefreshLeft();
        }

        private void OnSelectRightRoot()
        {
            var dialog = new FolderBrowserDialog();
            if (dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                UpdateRightRootPath(dialog.SelectedPath);
            }
        }

        private void UpdateRightRootPath(string path)
        {
            RightRootPath = path;
            OnRefreshRight();
        }

        private void OnAbout()
        {
            new WinAbout() { Owner = App.GetActiveWindow() }.ShowDialog();
        }

        private void OnSettings()
        {
            try
            {
                var win = new WinSettings { Owner = App.GetActiveWindow() };
                if (win.ShowDialog() == true)
                {
                    TextFileTypes = GetTextFileTypes();
                    RaisePropertyChanged(nameof(FontName));
                    RaisePropertyChanged(nameof(FontSize));
                    App.InitializeDatabase();
                }
            }
            catch (Exception ex)
            {
                LogHelper.Error(ex);
                MessageBox.Show("操作异常：" + ex.Message);
            }
        }
    }

    public enum EShowType
    {
        显示全部,
        显示差异项,
        显示相同项,
    }
}